#include "net/TimerQueue.h"
#include "net/EventLoop.h"
#include "net/TimerId.h"
#include "net/Timer.h"
#include "Base/Logger.h"
#include "Base/CurrectThread.h"
#include <sys/timerfd.h>
#include <unistd.h>
#include <errno.h>
#include <cstring>

namespace toym
{
namespace net
{

namespace detail
{

int createTimerFd() {
    int fd = ::timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
    if(fd < 0) {
        CurrectThread::saveErrno(errno);
        LOG_ERROR << "createTimerFd: timerfd_create failed";
        CurrectThread::saveErrno(0);
        ::abort();
    }
    LOG_DEBUG << "createTimerFd: " << fd;
    return fd;
}

void readTimerFd(int fd) {
    int64_t i64;
    int n = ::read(fd, &i64, sizeof(i64));
    if (n < 0) {
        CurrectThread::saveErrno(errno);
        LOG_ERROR << "readTimerFd: read fail";
        ::abort();
        CurrectThread::saveErrno(0);
    }
    if (n != 8) {
        LOG_WARNING << "readTimerFd " << fd 
                    << ", read " << n << " bytes"
                    "instead of 8";
    }
    LOG_DEBUG << "readTimerFd: fd-"<< fd;
}

} // namespace detail


///////////////////////////////////////////////
//////   Public methods
///////////////////////////////////////////////
TimerQueue::TimerQueue(EventLoop* loop)
    : m_loop(loop),
      m_timers(),
      m_timerFd(detail::createTimerFd()),
      m_timerChannel(m_timerFd, loop)
{
    m_timerChannel.setReadCallback([this](){
        this->handleTimers();
    });
    m_timerChannel.enbaleRead();
}

TimerQueue::~TimerQueue() {
    m_timerChannel.disableAll();
    m_loop->removeChannel(m_timerChannel);
    ::close(m_timerFd);
}

TimerId TimerQueue::addTimer(const TimerCallback_t& cb, Timestamp when, double interval) {
    auto timer = std::make_shared<Timer>(when, interval);
    TimerId ret = timer->getId();
    timer->setCallback(cb);
    insertTimer(timer);
    return ret;
}

TimerId TimerQueue::addTimer(TimerCallback_t&& cb, Timestamp when, double interval) {
    auto timer = std::make_shared<Timer>(when, interval);
    TimerId ret = timer->getId();
    timer->setCallback(std::move(cb));
    insertTimer(timer);
    return ret;
}

void TimerQueue::cancel(const TimerId& timerId) {
    LOG_WARNING << "TimerQueue::cancel unsupported";
}

///////////////////////////////////////////////
//////   Private methods
///////////////////////////////////////////////
void TimerQueue::handleTimers() {
    LOG_DEBUG << "handleTimers";
    m_loop->assertInLoopThread();
    detail::readTimerFd(m_timerFd);

    // 获取到期的timer
    Timestamp now = Timestamp::now();
    std::pair<Timestamp, std::shared_ptr<Timer>> sentry(now, std::shared_ptr<Timer>());
    std::vector<std::shared_ptr<Timer>> expiredTimers;

    auto itr = m_timers.lower_bound(sentry);
    auto p = m_timers.begin();
    while (p != itr) {
        expiredTimers.push_back(p->second);
        ++p;
    }
    m_timers.erase(m_timers.begin(), itr);

    // 调用
    for (auto& timer: expiredTimers) {
        timer->run();
        if (timer->getRepeat()) {
            timer->restart();
            insertTimer(timer);
        }
    }
    if (m_timers.size() > 0) {
        updateTimer();
    }
}

void TimerQueue::insertTimer(std::shared_ptr<Timer>& timer) {
    LOG_DEBUG << "insertTimer";
    m_loop->assertInLoopThread();
    Timestamp ts = timer->getExpired();
    std::pair<Timestamp, std::shared_ptr<Timer>> p(ts, timer);
    m_timers.insert(std::move(p));
    updateTimer();
}

void TimerQueue::updateTimer() {
    LOG_INFO << "updateTimer";

    m_loop->assertInLoopThread();
    struct itimerspec t;
    std::memset(&t, 0, sizeof(t));
    t.it_value.tv_nsec = 1;
    assert(m_timers.size() > 0);
    Timestamp ts = m_timers.begin()->first;
    Timestamp now = Timestamp::now();
    if (ts > now) {
        auto ticks = (ts.getTick() - now.getTick());
        t.it_value.tv_sec = ticks / Timestamp::s_tickPerSecond;
        t.it_value.tv_nsec = ticks % Timestamp::s_tickPerSecond * 1000L;
    }
    
    int ret = ::timerfd_settime(m_timerFd, 0,  &t, nullptr);
    if (ret < 0) {
        CurrectThread::saveErrno(errno);
        LOG_ERROR << "updateTimer: timerfd_settime fail";
        ::abort();
        CurrectThread::saveErrno(0);
    }
}



} // namespace net

} // namespace toym
